package com.ehome.poct.core.service.order;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.ehome.exception.BizException;
import com.ehome.paymentcenter.api.PayChannel;
import com.ehome.paymentcenter.api.pay.BaseRspCO;
import com.ehome.poct.base.api.constant.DeviceType;
import com.ehome.poct.base.api.dto.account.TermAgentOrgDTO;
import com.ehome.poct.base.api.dto.doctor.ClinicDTO;
import com.ehome.poct.base.api.enums.BusinessOrderTypeEnums;
import com.ehome.poct.base.api.enums.DeviceCheckTypeEnums;
import com.ehome.poct.base.api.enums.StripReportTypeEnums;
import com.ehome.poct.base.api.enums.StripTypeEnums;
import com.ehome.poct.base.api.qry.device.TestDeviceQry;
import com.ehome.poct.base.api.vo.device.TestDeviceVO;
import com.ehome.poct.core.api.constant.enums.ScanTypeEnums;
import com.ehome.poct.core.api.dto.check.TestRecordDetailDTO;
import com.ehome.poct.core.api.dto.check.TestReportDataConfigDTO;
import com.ehome.poct.core.api.dto.order.*;
import com.ehome.poct.core.api.dto.report.ProfitDTO;
import com.ehome.poct.core.api.qry.StripPriceProfitQry;
import com.ehome.poct.core.api.virtual.TestOrderInternalPayService;
import com.ehome.poct.core.api.virtual.TestOrderUnificationPayService;
import com.ehome.poct.core.api.vo.order.UnificationPayVO;
import com.ehome.poct.core.api.vo.order.UnificationShareAccountVO;
import com.ehome.poct.core.repository.order.TestOrderItemRepository;
import com.google.common.collect.Lists;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @auther: kuiba.peng
 * @date: 2022/11/21 21:06
 * @description:
 */
@Service
@RequiredArgsConstructor
public class TestOrderInternalPayServiceImpl implements TestOrderInternalPayService {

    private final TestOrderCombinationServiceImpl combinationService;

    @Resource
    private TestOrderUnificationPayService orderUnificationPayService;

    private final TestOrderItemRepository orderItemRepository;

    @Override
    public List<ProfitDTO> getPriceProfit(Long clinicId, List<Integer> stripCodeList) {
        if (CollUtil.isEmpty(stripCodeList)) {
            throw new BizException(400, "检测项不能为空");
        }

        stripCodeList = stripCodeList.stream().filter(Objects::nonNull).collect(Collectors.toList());
        if (CollUtil.isEmpty(stripCodeList)) {
            throw new BizException(400, "检测项不能为空");
        }


        StripPriceProfitQry profitQry = new StripPriceProfitQry();
        profitQry.setClinicId(clinicId);
        List<Integer> copyStripCodeList =Lists.newCopyOnWriteArrayList(stripCodeList);
        profitQry.setStripTypeCodes(copyStripCodeList);
        List<ProfitDTO> profitDTOList = combinationService.getProfitSchemeService().getProfitService(profitQry);

        List<TestReportDataConfigDTO> configDTOList = combinationService.getReportDataConfigRepository().findByStripeCodeIn(stripCodeList);

        rebuildReportDataConfig(configDTOList);

        Set<Integer> clinicProfitStripCodeList = profitDTOList.stream().map(ProfitDTO::getStripTypeCode).filter(Objects::nonNull).collect(Collectors.toSet());

        Collection<Integer> subtract = CollUtil.subtract(stripCodeList, clinicProfitStripCodeList);
        if (CollUtil.isNotEmpty(subtract)) {
            String unConfigProfitStripName = configDTOList.stream().filter(configDTO -> subtract.contains(configDTO.getStripTypeCode())).map(TestReportDataConfigDTO::getStripName).collect(Collectors.joining(","));
            throw new BizException(400, unConfigProfitStripName + " 暂无价格方案，请联系客服人员");
        }
        return profitDTOList;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<TestOrderItemDTO> convertOrderItems(Long clinicId, List<Integer> stripCodeList, String orderNo) {
        List<ProfitDTO> profitDTOList = getPriceProfit(clinicId, stripCodeList);

        List<TestReportDataConfigDTO> dataConfigDTOList = combinationService.getReportDataConfigRepository().list();

        List<Integer> profitStripCodeList = profitDTOList.stream().map(ProfitDTO::getStripTypeCode).collect(Collectors.toList());

        // 获取订单子商品信息
        List<TestOrderItemDTO> orderItemList = getOrderItemList(profitStripCodeList, profitDTOList, dataConfigDTOList);

        orderItemList.forEach(orderItem -> orderItem.setTestOrderNo(orderNo));

        // 获取分账信息
        saveShareAccount(orderItemList, profitDTOList, clinicId);

        return orderItemList;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public UnificationShareAccountVO saveShareAccount(List<TestOrderItemDTO> orderItemDTOList, List<ProfitDTO> profitDTOList, Long clinicId) {
        List<UnificationShareAccountDTO> shareAccountDTOList = getBusinessShareAccountInfo(clinicId, profitDTOList, orderItemDTOList);
        if (CollUtil.isEmpty(orderItemDTOList)) {
            return new UnificationShareAccountVO().setOrderNo(null).setUnificationShareAccountDTOList(Lists.newArrayList());
        }
        return combinationService.getUnificationPayRepository().saveShareAccount(shareAccountDTOList, orderItemDTOList.get(0).getTestOrderNo());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public UnificationPayVO internalPay(TestOrderDTO orderDTO, ScanTypeEnums scanType, String authCode) {
        String payNotifyUrl = combinationService.getPayProperties().getNotifyUrl();
        if (BusinessOrderTypeEnums.PRE_TRADE.equals(orderDTO.getBusinessOrderType())) {
            payNotifyUrl = combinationService.getPayProperties().getPreNotifyUrl();
        }

        UnificationPayReqDTO unificationPayReqDTO = combinationService.getOrderMapping().toPayReqDTO(orderDTO, combinationService.getPayProperties().getTimeout() + "m", payNotifyUrl, "血常规订单");
        unificationPayReqDTO.setAuthCode(authCode);
        unificationPayReqDTO.setScanType(scanType);

        List<TestOrderItemDTO> itemDTOList = orderItemRepository.list(new TestOrderItemDTO().setTestOrderNo(orderDTO.getTestOrderNo()));
        unificationPayReqDTO.setOrderItemList(itemDTOList);

        List<TestPoctProfitsRecordDTO> shareAccountList = combinationService.getUnificationPayRepository().getShareAccountList(orderDTO.getTestOrderNo());
        unificationPayReqDTO.setProfitsRecordList(shareAccountList);

        UnificationPayVO unificationPayVO = orderUnificationPayService.pay(unificationPayReqDTO);

        if (Objects.nonNull(unificationPayVO.getPayInfo())) {
            BaseRspCO baseRspCO = BeanUtil.copyProperties(unificationPayVO.getPayInfo(), BaseRspCO.class);
            orderDTO.setOutOrderNo(baseRspCO.getOutOrderNo());
            orderDTO.setPayChannel(Optional.ofNullable(baseRspCO.getPayChannel()).map(PayChannel::getType).orElse(null));
        }
        return unificationPayVO;
    }

    private List<TestOrderItemDTO> getOrderItemList(List<Integer> stripCodeList, List<ProfitDTO> profitDTOList, List<TestReportDataConfigDTO> dataConfigDTOList) {
        Map<Integer, ProfitDTO> profitDTOMap = profitDTOList.stream().collect(Collectors.toMap(ProfitDTO::getStripTypeCode, profitDTO -> profitDTO, (o1, o2) -> o1));

        // 单测
        List<TestOrderItemDTO> singleItemList = getSingleItemList(stripCodeList, profitDTOMap, dataConfigDTOList);
        List<TestOrderItemDTO> allItemList = new ArrayList<>(singleItemList);

        // 联测报告
        List<TestOrderItemDTO> unitReportItems = getUnitReportItems(stripCodeList, dataConfigDTOList);
        allItemList.addAll(unitReportItems);

        //特殊联测报告  （SAA+CRP）、(三联测)
        List<TestOrderItemDTO> specialReportItemList = getSpecialReport(stripCodeList, profitDTOMap);
        allItemList.addAll(specialReportItemList);

        List<TestOrderItemDTO> combinationReportItemList = getCombinationReport(profitDTOList, profitDTOMap);

        //删除存在的报告（因为价格为0，以组合的价格为准）
        if (CollUtil.isNotEmpty(combinationReportItemList)) {
            Set<String> reportTypeCodeSet = combinationReportItemList.stream()
                    .map(TestOrderItemDTO::getTestReportTypeCode)
                    .filter(Objects::nonNull).collect(Collectors.toSet());
            allItemList.removeIf(orderDTO -> reportTypeCodeSet.contains(orderDTO.getTestReportTypeCode()));
        }
        allItemList.addAll(combinationReportItemList);

        return allItemList;
    }

    /**
     * 获取组合代码报告
     *
     * @param profitDTOList
     * @param profitDTOMap
     * @return
     */
    private List<TestOrderItemDTO> getCombinationReport(List<ProfitDTO> profitDTOList, Map<Integer, ProfitDTO> profitDTOMap) {
        Set<Integer> profitCodeSet = profitDTOList.stream().map(ProfitDTO::getStripTypeCode).collect(Collectors.toSet());
        if (!CollUtil.containsAny(profitCodeSet, BUNDLING_STRIP_CODE_LIST)) {
            return Lists.newArrayList();
        }
        List<TestOrderItemDTO> combinationReportItems = new ArrayList<>(BUNDLING_STRIP_CODE_LIST.size());
        BUNDLING_REPORT_LIST.forEach(stripType -> {
            ProfitDTO profitDTO = profitDTOMap.get(stripType.getStripTypeCode());
            if (Objects.isNull(profitDTO)) {
                return;
            }
            combinationReportItems.add(convertOrderItem(profitDTO, 2, stripType.getReportTypeCode()));
        });
        return combinationReportItems;
    }

    /**
     * 获取联测报告
     *
     * @param stripCodeList
     * @param dataConfigDTOList
     */
    public List<TestOrderItemDTO> getUnitReportItems(List<Integer> stripCodeList, List<TestReportDataConfigDTO> dataConfigDTOList) {
        //report type等于2代表联测报告；不等于4代表 非双联试纸
        List<TestReportDataConfigDTO> unitDataConfigList = dataConfigDTOList.stream()
                .filter(configDTO -> isUnitReportFilter(stripCodeList, configDTO))
                .collect(Collectors.toList());
        //按照检测编码进行分组
        Map<String, List<TestReportDataConfigDTO>> reportDataConfigGroupMap = unitDataConfigList.stream().collect(Collectors.groupingBy(TestReportDataConfigDTO::getReportCode));
        //先拿到所有联测包含订单项
        return reportDataConfigGroupMap.values().stream()
                .filter(configDTOList -> configDTOList.size() >= 2)
                .map(configDTOList -> combinationService.getPreOrderMapping().to(configDTOList.get(0), null, BigDecimal.ZERO, null))
                .collect(Collectors.toList());
    }

    /**
     * 判断是否符合联测条件
     * reportCode 等于2 代表联测报告
     * testPropertyType 不等于4 代表非双联试纸
     *
     * @param stripCodeList
     * @param configDTO
     * @return
     */
    private static boolean isUnitReportFilter(List<Integer> stripCodeList, TestReportDataConfigDTO configDTO) {
        return stripCodeList.contains(configDTO.getStripTypeCode())
                && configDTO.getReportType().equals(StripReportTypeEnums.UNIT.getReportType())
                && !configDTO.getTestPropertyType().equals(4);
    }

    /**
     * 获取单测检测项
     *
     * @param stripCodeList
     * @param profitDTOMap
     * @param dataConfigDTOList
     * @return
     */
    private List<TestOrderItemDTO> getSingleItemList(List<Integer> stripCodeList, Map<Integer, ProfitDTO> profitDTOMap, List<TestReportDataConfigDTO> dataConfigDTOList) {
        return dataConfigDTOList.stream()
                .filter(configDTO -> singleReportConfigFilter(stripCodeList, configDTO))
                .map(configDTO -> {
                    TestOrderItemDTO orderItemDTO = combinationService.getPreOrderMapping().to(configDTO, configDTO.getStripTypeCode(), BigDecimal.ZERO, null);
                    ProfitDTO profitDTO = profitDTOMap.get(configDTO.getStripTypeCode());
                    Optional.ofNullable(profitDTO).ifPresent(profit -> {
                        orderItemDTO.setFee(profitDTO.getSalePrice());
                        if (StrUtil.isBlank(orderItemDTO.getTestReportTypeCode())) {
                            orderItemDTO.setTestReportTypeCode(profitDTO.getReportCode());
                        }
                    });
                    return orderItemDTO;
                }).collect(Collectors.toList());
    }

    /**
     * 获取特殊的联测报告
     * （18 ==  SAA + CRP）；（121 == SAA + CRP + 血常规）
     *
     * @param stripCodeList
     * @param profitDTOMap
     * @return
     */
    private List<TestOrderItemDTO> getSpecialReport(List<Integer> stripCodeList, Map<Integer, ProfitDTO> profitDTOMap) {
        Collection<Integer> intersection = CollUtil.intersection(SPECIAL_REPORT_STRIP_CODE_LIST, stripCodeList);
        if (CollUtil.isEmpty(intersection)) {
            return Lists.newArrayList();
        }
        List<TestOrderItemDTO> specialReportItemList = Lists.newArrayListWithCapacity(SPECIAL_REPORT_LIST.size());
        SPECIAL_REPORT_LIST.forEach(stripType -> {
            ProfitDTO profitDTO = profitDTOMap.get(stripType.getStripTypeCode());
            if (Objects.isNull(profitDTO)) {
                return;
            }
            // 血常规
            if (StripTypeEnums.BLD.equals(stripType)) {
                specialReportItemList.add(convertOrderItem(profitDTO, profitDTO.getTestPoctReportType(), stripType.getReportTypeCode()));
                return;
            }
            // 联测
            specialReportItemList.add(convertOrderItem(profitDTO, profitDTO.getTestPoctReportType(), stripType.getReportTypeCode()));
        });
        return specialReportItemList;
    }


    /**
     * 单测报告过滤
     *
     * @param stripCodeList
     * @param configDTO
     * @return
     */
    private boolean singleReportConfigFilter(List<Integer> stripCodeList, TestReportDataConfigDTO configDTO) {
        return stripCodeList.contains(configDTO.getStripTypeCode())
                && stripCodeList.contains(configDTO.getRealStripTypeCode())
                && configDTO.getReportType().equals(StripReportTypeEnums.SINGLE.getReportType());
    }

    /**
     * 转换订单子商品信息
     *
     * @param profitDTO
     * @param testPoctReportType
     * @return
     */
    @SuppressWarnings("SameParameterValue")
    private TestOrderItemDTO convertOrderItem(ProfitDTO profitDTO, int testPoctReportType, String reportTypeCode) {
        TestOrderItemDTO orderItem = new TestOrderItemDTO();
        orderItem.setOrderItemName(profitDTO.getTestConfStripName());
        orderItem.setFee(profitDTO.getSalePrice());
        orderItem.setTestPoctReportType(testPoctReportType);
        orderItem.setTestReportTypeCode(reportTypeCode);
        orderItem.setStripTypeCode(profitDTO.getStripTypeCode());
        return orderItem;
    }

    /**
     * 获取分账信息
     *
     * @param clinicId
     * @param profitDTOList
     * @param orderItemList
     * @return
     */
    private List<UnificationShareAccountDTO> getBusinessShareAccountInfo(Long clinicId, List<ProfitDTO> profitDTOList, List<TestOrderItemDTO> orderItemList) {
        final ClinicDTO clinicDTO = combinationService.getClinicClient().getById(clinicId);
        final TermAgentOrgDTO agentOrgDTO = combinationService.getAgentOrgClient().getByAgentId(clinicDTO.getTermAgentId());
        List<UnificationShareAccountDTO> shareAccountDTOList = new ArrayList<>(orderItemList.size());
        Map<Integer, ProfitDTO> profitDTOMap = profitDTOList.stream().collect(Collectors.toMap(ProfitDTO::getStripTypeCode, profitDTO -> profitDTO, (o1, o2) -> o1));
        orderItemList.stream()
                // 过滤金额小于等于0的数据
                .filter(orderItem -> Objects.nonNull(orderItem.getFee()) && orderItem.getFee().compareTo(BigDecimal.ZERO) > 0)
                .forEach(orderItem -> {
                    ProfitDTO profitDTO = profitDTOMap.get(orderItem.getStripTypeCode());
                    if (Objects.isNull(profitDTO)) {
                        return;
                    }
                    orderItem.setDeviceType(getDeviceType(orderItem.getStripTypeCode()));
                    if (StrUtil.isEmpty(orderItem.getOrderItemName())) {
                        orderItem.setOrderItemName(profitDTO.getTestConfStripName());
                    }
                    UnificationShareAccountDTO shareAccountDTO = combinationService.getShareAccountMapping().toShareAccount(clinicDTO, orderItem, profitDTO, agentOrgDTO);
                    shareAccountDTOList.add(shareAccountDTO);
                });
        return shareAccountDTOList;
    }

    /**
     * 获设备类型
     *
     * @param stripCode
     * @return
     */
    private Integer getDeviceType(Integer stripCode) {
        if (DeviceCheckTypeEnums.XCG.getStripCodeList().contains(stripCode)) {
            return DeviceCheckTypeEnums.XCG.getDeviceType();
        }
        return DeviceCheckTypeEnums.JBY.getDeviceType();
    }


    /**
     * @param placeOrder
     * @param orderItems
     * @return
     */
    public List<TestRecordDetailDTO> convertRecordDetailList(PrePlaceOrderDTO placeOrder, List<TestOrderItemDTO> orderItems) {
        List<TestReportDataConfigDTO> reportConfigList = combinationService.getReportDataConfigRepository().list();
        List<TestOrderItemDTO> bloodOrderItemList = orderItems.stream().filter(orderItemDTO -> BLOOD_STRIP_CODE_LIST.contains(orderItemDTO.getStripTypeCode())).collect(Collectors.toList());
        List<TestDeviceVO> deviceVOList = combinationService.getDeviceClient().list(new TestDeviceQry().setTermClinicId(placeOrder.getClinicId()));
        // 暂时不做校验  懒得写了  todo check
        // 按设备类型转map 禁止覆盖，防止出现多个相同类型设备
        Map<Integer, String> deviceTypeMap = deviceVOList.stream().collect(Collectors.toMap(TestDeviceVO::getDeviceType, TestDeviceVO::getSno, (o1, o2) -> o1));
        return orderItems.stream()
                // 过滤活动捆绑的检查项
                .filter(it -> it.getFee().signum() == 1 || StripTypeEnums.SAA_CRP_SINGLE_PAPER.getReportTypeCode().equals(it.getTestReportTypeCode()))
                .map(it -> {
                    final var peoplePortion = reportConfigList.stream()
                            .filter(t -> t.getReportCode().equals(it.getTestReportTypeCode())).findFirst()
                            .map(TestReportDataConfigDTO::getTestPeoplePortion).orElse(BigDecimal.ONE);
                    TestRecordDetailDTO recordDetailDTO = combinationService.getPreOrderMapping().to(it, peoplePortion, placeOrder, bloodOrderItemList.size() > 0 ? 2 : 1);
                    if (StripTypeEnums.SAA_CRP_SINGLE_PAPER.getReportTypeCode().equals(it.getTestReportTypeCode())) {
                        recordDetailDTO.setStripTypeCode(StripTypeEnums.SAA_CRP.getStripTypeCode());
                    }
                    settingSno(deviceTypeMap, it, recordDetailDTO);
                    return recordDetailDTO;
                }).collect(Collectors.toList());
    }


    /**
     * 设置设备sno
     *
     * @param deviceTypeMap
     * @param orderItemDTO
     * @param recordDetailDTO
     */
    private void settingSno(Map<Integer, String> deviceTypeMap, TestOrderItemDTO orderItemDTO, TestRecordDetailDTO recordDetailDTO) {
        recordDetailDTO.setSno(deviceTypeMap.get(DeviceType.JBY.getDeviceType()));
        if (StripTypeEnums.BLD.getStripTypeCode().equals(orderItemDTO.getStripTypeCode())) {
            recordDetailDTO.setSno(deviceTypeMap.get(DeviceType.BLOOD.getDeviceType()));
        }
        if (StripTypeEnums.U_CRP_SAA_BLD_REPORT.getStripTypeCode().equals(orderItemDTO.getStripTypeCode())) {
            recordDetailDTO.setSno(deviceTypeMap.get(DeviceType.BLOOD.getDeviceType()));
            recordDetailDTO.setSno2(deviceTypeMap.get(DeviceType.JBY.getDeviceType()));
        }
    }

    /**
     * 重新构建检测项配置
     *
     * @param configDTOList
     */
    private void rebuildReportDataConfig(List<TestReportDataConfigDTO> configDTOList) {
        List<TestReportDataConfigDTO> filterReportDataConfigList = configDTOList.stream().filter(configDTO -> {
            // crp saa特殊处理 TestReportDataConfigDTO存在多条检测项相同的数据
            if (StripTypeEnums.SAA.getStripTypeCode().equals(configDTO.getStripTypeCode())
                    || StripTypeEnums.CRP.getStripTypeCode().equals(configDTO.getStripTypeCode())) {
                return StripReportTypeEnums.SINGLE.getReportType().equals(configDTO.getReportType());
            }
            return true;
        }).collect(Collectors.toList());
        configDTOList.clear();
        configDTOList.addAll(filterReportDataConfigList);

        TestReportDataConfigDTO bldDataConfig = new TestReportDataConfigDTO();
        bldDataConfig.setStripTypeCode(StripTypeEnums.BLD.getStripTypeCode());
        bldDataConfig.setStripName(StripTypeEnums.BLD.getDesc());
        configDTOList.add(bldDataConfig);

        TestReportDataConfigDTO threeStripConfig = new TestReportDataConfigDTO();
        threeStripConfig.setStripTypeCode(StripTypeEnums.U_CRP_SAA_BLD_REPORT.getStripTypeCode());
        threeStripConfig.setStripName("三联测");
        configDTOList.add(threeStripConfig);
    }
}
